#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <sys/utsname.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <fstream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

#define VPN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), vpn_plugin_get_type(), VpnPlugin))

struct _VpnPlugin {
  GObject parent_instance;
  pid_t xray_pid;
};

G_DEFINE_TYPE(VpnPlugin, vpn_plugin, g_object_get_type())

/**
 * VPN 插件 - Linux 平台实现
 * 提供与 V2Ray/Xray 核心的交互接口
 */

// 方法调用处理
static FlMethodResponse* start_xray(VpnPlugin* self, FlValue* args);
static FlMethodResponse* stop_xray(VpnPlugin* self);
static FlMethodResponse* get_traffic_stats(VpnPlugin* self);

// 处理方法调用
static void vpn_plugin_handle_method_call(
    VpnPlugin* self,
    FlMethodCall* method_call) {
  
  const gchar* method = fl_method_call_get_name(method_call);
  FlValue* args = fl_method_call_get_args(method_call);

  g_autoptr(FlMethodResponse) response = nullptr;

  if (strcmp(method, "startXray") == 0) {
    response = start_xray(self, args);
  } else if (strcmp(method, "stopXray") == 0) {
    response = stop_xray(self);
  } else if (strcmp(method, "getTrafficStats") == 0) {
    response = get_traffic_stats(self);
  } else {
    response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
  }

  fl_method_call_respond(method_call, response, nullptr);
}

/**
 * 启动 Xray 核心
 */
static FlMethodResponse* start_xray(VpnPlugin* self, FlValue* args) {
  if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
    return FL_METHOD_RESPONSE(fl_method_error_response_new(
        "INVALID_ARGUMENT", "Arguments must be a map", nullptr));
  }

  FlValue* config_value = fl_value_lookup_string(args, "config");
  if (config_value == nullptr || fl_value_get_type(config_value) != FL_VALUE_TYPE_STRING) {
    return FL_METHOD_RESPONSE(fl_method_error_response_new(
        "INVALID_ARGUMENT", "Config is required", nullptr));
  }

  const char* config = fl_value_get_string(config_value);

  try {
    // 1. 写入配置文件
    std::string config_path = "/tmp/xray_config.json";
    std::ofstream config_file(config_path);
    if (!config_file.is_open()) {
      return FL_METHOD_RESPONSE(fl_method_error_response_new(
          "FILE_ERROR", "Cannot create config file", nullptr));
    }
    config_file << config;
    config_file.close();

    std::cout << "Config written to: " << config_path << std::endl;

    // 2. 查找 xray 可执行文件
    std::string xray_path;
    const char* possible_paths[] = {
      "./xray-core/xray",
      "./xray",
      "/usr/bin/xray",
      "/usr/local/bin/xray",
      nullptr
    };

    for (int i = 0; possible_paths[i] != nullptr; i++) {
      if (access(possible_paths[i], X_OK) == 0) {
        xray_path = possible_paths[i];
        break;
      }
    }

    if (xray_path.empty()) {
      return FL_METHOD_RESPONSE(fl_method_error_response_new(
          "NOT_FOUND", "Xray executable not found", nullptr));
    }

    std::cout << "Found xray at: " << xray_path << std::endl;

    // 3. 启动 xray 进程
    pid_t pid = fork();
    if (pid < 0) {
      return FL_METHOD_RESPONSE(fl_method_error_response_new(
          "FORK_ERROR", "Failed to fork process", nullptr));
    } else if (pid == 0) {
      // 子进程：执行 xray
      execl(xray_path.c_str(), "xray", "run", "-config", config_path.c_str(), nullptr);
      // 如果 execl 返回，说明出错了
      exit(1);
    } else {
      // 父进程：保存 PID
      self->xray_pid = pid;
      std::cout << "Xray started with PID: " << pid << std::endl;
    }

    g_autoptr(FlValue) result = fl_value_new_bool(TRUE);
    return FL_METHOD_RESPONSE(fl_method_success_response_new(result));

  } catch (const std::exception& e) {
    return FL_METHOD_RESPONSE(fl_method_error_response_new(
        "START_ERROR", e.what(), nullptr));
  }
}

/**
 * 停止 Xray 核心
 */
static FlMethodResponse* stop_xray(VpnPlugin* self) {
  try {
    if (self->xray_pid > 0) {
      std::cout << "Stopping Xray process (PID: " << self->xray_pid << ")" << std::endl;
      
      // 发送 SIGTERM 信号
      kill(self->xray_pid, SIGTERM);
      
      // 等待进程退出（最多 5 秒）
      int status;
      for (int i = 0; i < 50; i++) {
        pid_t result = waitpid(self->xray_pid, &status, WNOHANG);
        if (result != 0) {
          break;
        }
        usleep(100000); // 等待 100ms
      }
      
      // 如果还没退出，强制杀死
      if (waitpid(self->xray_pid, &status, WNOHANG) == 0) {
        std::cout << "Force killing Xray process" << std::endl;
        kill(self->xray_pid, SIGKILL);
        waitpid(self->xray_pid, &status, 0);
      }
      
      self->xray_pid = 0;
      std::cout << "Xray stopped successfully" << std::endl;
    }

    g_autoptr(FlValue) result = fl_value_new_bool(TRUE);
    return FL_METHOD_RESPONSE(fl_method_success_response_new(result));

  } catch (const std::exception& e) {
    return FL_METHOD_RESPONSE(fl_method_error_response_new(
        "STOP_ERROR", e.what(), nullptr));
  }
}

/**
 * 获取流量统计
 */
static FlMethodResponse* get_traffic_stats(VpnPlugin* self) {
  g_autoptr(FlValue) stats = fl_value_new_map();
  
  fl_value_set_string_take(stats, "uploadSpeed", fl_value_new_int(0));
  fl_value_set_string_take(stats, "downloadSpeed", fl_value_new_int(0));
  fl_value_set_string_take(stats, "totalUpload", fl_value_new_int(0));
  fl_value_set_string_take(stats, "totalDownload", fl_value_new_int(0));

  return FL_METHOD_RESPONSE(fl_method_success_response_new(stats));
}

static void vpn_plugin_dispose(GObject* object) {
  VpnPlugin* self = VPN_PLUGIN(object);
  
  // 清理：停止 xray 进程
  if (self->xray_pid > 0) {
    kill(self->xray_pid, SIGTERM);
    self->xray_pid = 0;
  }

  G_OBJECT_CLASS(vpn_plugin_parent_class)->dispose(object);
}

static void vpn_plugin_class_init(VpnPluginClass* klass) {
  G_OBJECT_CLASS(klass)->dispose = vpn_plugin_dispose;
}

static void vpn_plugin_init(VpnPlugin* self) {
  self->xray_pid = 0;
}

VpnPlugin* vpn_plugin_new() {
  return VPN_PLUGIN(g_object_new(vpn_plugin_get_type(), nullptr));
}

// 注册插件
void vpn_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
  VpnPlugin* plugin = vpn_plugin_new();

  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
  g_autoptr(FlMethodChannel) channel =
      fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
                           "com.fimuer.vpn/core",
                           FL_METHOD_CODEC(codec));
  
  fl_method_channel_set_method_call_handler(
      channel, 
      (FlMethodChannelMethodCallHandler)vpn_plugin_handle_method_call,
      g_object_ref(plugin),
      g_object_unref);

  g_object_unref(plugin);
}
